import os
import shutil
import subprocess
import sys
import tarfile
import threading
import time
import tkinter as tk
import zipfile
from tkinter import ttk, scrolledtext, filedialog, messagebox, simpledialog

import py7zr
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View

@annotate(ClassInfo(name="文件查看", category="file", plug=False))
class FileView(View):
    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 初始化变量
        self.current_path = None
        self.search_query = ""
        self.text_editing_path = None
        self.drives = []
        self.supported_archives = ["zip", "tar.gz", "tar.bz2", "7z"]

        # 创建UI
        self._create_widgets()

        # 加载文件系统
        # self.load_file_system()

    def _create_widgets(self):
        """创建界面组件"""
        # 主容器
        main_container = ttk.Frame(self, padding=10)
        main_container.pack(fill=BOTH, expand=YES)

        # 顶部工具栏
        toolbar_frame = ttk.LabelFrame(main_container, text="操作", padding=10)
        toolbar_frame.pack(fill=X, pady=5)

        # 搜索框
        search_frame = ttk.Frame(toolbar_frame)
        search_frame.pack(side=LEFT, fill=X, expand=YES, padx=5)

        ttk.Label(search_frame, text="搜索:").pack(side=LEFT, padx=5)
        self.search_var = tk.StringVar()
        self.search_var.trace_add("write", self.on_search_changed)
        search_entry = ttk.Entry(search_frame, textvariable=self.search_var)
        search_entry.pack(side=LEFT, fill=X, expand=YES, padx=5)

        # 操作按钮 - 第一组
        btn_frame1 = ttk.Frame(toolbar_frame)
        btn_frame1.pack(side=RIGHT, padx=5)

        ttk.Button(
            btn_frame1,
            text="添加文件",
            command=self.add_files,
            bootstyle=SUCCESS,
            width=10
        ).pack(side=LEFT, padx=2)

        ttk.Button(
            btn_frame1,
            text="新建文件夹",
            command=self.create_folder,
            bootstyle=INFO,
            width=10
        ).pack(side=LEFT, padx=2)

        ttk.Button(
            btn_frame1,
            text="删除",
            command=self.delete_selected,
            bootstyle=DANGER,
            width=10
        ).pack(side=LEFT, padx=2)

        # 操作按钮 - 第二组（压缩解压相关）
        btn_frame2 = ttk.Frame(toolbar_frame)
        btn_frame2.pack(side=RIGHT, padx=5)

        ttk.Button(
            btn_frame2,
            text="压缩",
            command=self.compress_selected,
            bootstyle=PRIMARY,
            width=10
        ).pack(side=LEFT, padx=2)

        ttk.Button(
            btn_frame2,
            text="解压",
            command=self.extract_selected,
            bootstyle=SECONDARY,
            width=10
        ).pack(side=LEFT, padx=2)

        ttk.Button(
            btn_frame2,
            text="重命名",
            command=self.rename_selected,
            bootstyle=SECONDARY,
            width=10
        ).pack(side=LEFT, padx=2)

        ttk.Button(
            btn_frame2,
            text="打开",
            command=self.open_selected,
            bootstyle=PRIMARY,
            width=10
        ).pack(side=LEFT, padx=2)

        # 中间内容区域（分左右两部分）
        content_frame = ttk.Frame(main_container)
        content_frame.pack(fill=BOTH, expand=YES, pady=5)

        # 左侧：文件系统树形结构
        left_frame = ttk.LabelFrame(content_frame, text="文件系统", padding=10)
        left_frame.pack(side=LEFT, fill=BOTH, expand=NO, padx=5)

        ttk.Button(
            left_frame,
            text="刷新",
            command=self.load_file_system,
            bootstyle=PRIMARY,
            width=10
        ).pack(side=TOP, padx=2,pady=5)

        # 树形视图
        self.file_tree = ttk.Treeview(left_frame, show="tree")
        self.file_tree.pack(side=LEFT, fill=BOTH, expand=YES)

        # 滚动条
        tree_scroll = ttk.Scrollbar(left_frame, orient=VERTICAL, command=self.file_tree.yview)
        tree_scroll.pack(side=RIGHT, fill=Y)
        self.file_tree.configure(yscroll=tree_scroll.set)

        # 绑定树形视图事件
        self.file_tree.bind("<<TreeviewSelect>>", self.on_tree_select)
        self.file_tree.bind("<Double-1>", lambda e: self.on_tree_double_click(e))

        # 右侧：文件列表和文本编辑器
        right_frame = ttk.Frame(content_frame)
        right_frame.pack(side=RIGHT, fill=BOTH, expand=YES, padx=5)

        # 文件列表区域
        files_frame = ttk.LabelFrame(right_frame, text="文件列表", padding=10)
        files_frame.pack(fill=BOTH, expand=YES, pady=5)

        # 文件列表视图
        columns = ("name", "type", "size", "modified")
        self.files_list = ttk.Treeview(
            files_frame,
            columns=columns,
            show="headings",
            selectmode="extended"
        )

        # 设置列标题和宽度
        self.files_list.heading("name", text="名称")
        self.files_list.heading("type", text="类型")
        self.files_list.heading("size", text="大小")
        self.files_list.heading("modified", text="修改日期")

        self.files_list.column("name", width=250, anchor=W)
        self.files_list.column("type", width=100, anchor=W)
        self.files_list.column("size", width=100, anchor=E)
        self.files_list.column("modified", width=150, anchor=W)

        # 文件列表滚动条
        files_scroll_y = ttk.Scrollbar(files_frame, orient=VERTICAL, command=self.files_list.yview)
        files_scroll_x = ttk.Scrollbar(files_frame, orient=HORIZONTAL, command=self.files_list.xview)
        self.files_list.configure(yscroll=files_scroll_y.set, xscroll=files_scroll_x.set)

        # 布局文件列表
        self.files_list.pack(side=TOP, fill=BOTH, expand=YES)
        files_scroll_y.pack(side=RIGHT, fill=Y)
        files_scroll_x.pack(side=BOTTOM, fill=X)

        # 绑定文件列表事件
        self.files_list.bind("<<TreeviewSelect>>", self.on_files_select)
        self.files_list.bind("<Double-1>", lambda e: self.on_files_double_click(e))

        # 文本编辑器区域（默认隐藏）
        self.text_editor_frame = ttk.LabelFrame(right_frame, text="文本编辑", padding=10)
        # 初始不显示，编辑文本时再显示

        # 文本编辑器工具栏
        editor_toolbar = ttk.Frame(self.text_editor_frame)
        editor_toolbar.pack(fill=X, pady=5)

        ttk.Button(
            editor_toolbar,
            text="保存",
            command=self.save_text_file,
            bootstyle=SUCCESS,
            width=10
        ).pack(side=LEFT, padx=5)

        ttk.Button(
            editor_toolbar,
            text="关闭编辑",
            command=self.close_text_editor,
            bootstyle=SECONDARY,
            width=10
        ).pack(side=LEFT, padx=5)

        # 文本编辑器
        self.text_editor = scrolledtext.ScrolledText(self.text_editor_frame, wrap=tk.WORD)
        self.text_editor.pack(fill=BOTH, expand=YES, pady=5)

        # 进度条（默认隐藏）
        self.progress_frame = ttk.Frame(main_container)
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(
            self.progress_frame,
            variable=self.progress_var,
            maximum=100
        )
        self.progress_label = ttk.Label(self.progress_frame, text="准备中...")

        self.progress_label.pack(side=LEFT, padx=10)
        self.progress_bar.pack(side=LEFT, fill=X, expand=YES, padx=10)

    def show_progress(self, message):
        """显示进度条"""
        self.progress_label.config(text=message)
        self.progress_var.set(0)
        self.progress_frame.pack(fill=X, pady=5)
        self.update_idletasks()

    def update_progress(self, value, message=None):
        """更新进度条"""
        self.progress_var.set(value)
        if message:
            self.progress_label.config(text=message)
        self.update_idletasks()

    def hide_progress(self):
        """隐藏进度条"""
        self.progress_frame.pack_forget()

    def load_file_system(self):
        """加载文件系统树形结构"""
        # 清空现有内容
        for item in self.file_tree.get_children():
            self.file_tree.delete(item)

        # 根据操作系统加载盘符或根目录
        if sys.platform.startswith('win'):
            # Windows系统 - 获取所有盘符
            self.drives = []
            for drive in range(65, 91):  # A-Z
                drive_letter = chr(drive) + ":"
                if os.path.exists(drive_letter):
                    self.drives.append(drive_letter)
                    # 添加到树形视图
                    self.file_tree.insert("", tk.END, id=drive_letter, text=drive_letter)
                    # 添加一个临时子节点作为加载指示器
                    self.file_tree.insert(drive_letter, tk.END, id=f"{drive_letter}_loading", text="-")
        else:
            # Linux/macOS系统 - 从根目录开始
            root = "/"
            self.file_tree.insert("", tk.END, id=root, text=root)
            self.file_tree.insert(root, tk.END, id=f"{root}_loading", text="-")

        # 启动线程加载目录内容（避免UI卡顿）
        threading.Thread(target=self.load_drives_content, daemon=True).start()

    def load_drives_content(self):
        """加载所有盘符的内容（在后台线程中执行）"""
        if sys.platform.startswith('win'):
            for drive in self.drives:
                self.load_directory_content(drive)
        else:
            self.load_directory_content("/")

    def load_directory_content(self, path, parent=None):
        """加载目录内容到树形视图"""
        if not parent:
            parent = path

        # 移除加载指示器
        loading_id = f"{parent}_loading"
        if self.file_tree.exists(loading_id):
            self.file_tree.delete(loading_id)

        try:
            # 获取目录内容
            items = os.listdir(path)
            folders = []

            # 先添加文件夹
            for item in items:
                item_path = os.path.join(path, item)
                if os.path.isdir(item_path) and not item.startswith('.'):  # 忽略隐藏文件夹
                    folders.append(item)
                    item_id = f"{parent}/{item}"
                    # 替换特殊字符，避免ID冲突
                    item_id = item_id.replace(" ", "_").replace(".", "_")
                    self.file_tree.insert(parent, tk.END, id=item_id, text=item)
                    # 添加加载指示器
                    self.file_tree.insert(item_id, tk.END, id=f"{item_id}_loading", text="-")

            # 按名称排序
            self.sort_tree_items(parent)

        except Exception as e:
            # 显示错误信息
            self.file_tree.insert(parent, tk.END, text=f"无法访问: {str(e)}", tags=("error",))
            self.file_tree.tag_configure("error", foreground="red")

    def sort_tree_items(self, parent):
        """对树形视图中的项目进行排序"""
        children = self.file_tree.get_children(parent)
        # 按文本排序
        sorted_children = sorted(children, key=lambda x: self.file_tree.item(x, "text").lower())

        # 重新排列
        for index, child in enumerate(sorted_children):
            self.file_tree.move(child, parent, index)

    def on_tree_select(self, event):
        """当树形视图选择改变时触发"""
        selected = self.file_tree.selection()
        if selected:
            path = self.get_path_from_tree_id(selected[0])
            if os.path.isdir(path):
                self.current_path = path
                self.display_directory_content(path)

    def on_tree_double_click(self, event):
        """双击树形视图项目时触发"""
        item = self.file_tree.identify_row(event.y)
        if item:
            path = self.get_path_from_tree_id(item)
            if os.path.isdir(path):
                # 如果没有加载子目录，则加载
                if len(self.file_tree.get_children(item)) == 1 and self.file_tree.get_children(item)[0].endswith(
                        "_loading"):
                    threading.Thread(target=self.load_directory_content, args=(path, item), daemon=True).start()
                # 切换展开/折叠状态
                if self.file_tree.item(item, "open"):
                    self.file_tree.item(item, open=False)
                else:
                    self.file_tree.item(item, open=True)

    def get_path_from_tree_id(self, tree_id):
        """从树形视图ID转换为实际路径"""
        # 移除特殊字符恢复原始路径
        path = tree_id.replace("_", " ").replace("__", ".")

        # 处理Windows盘符
        if sys.platform.startswith('win') and len(path) > 1 and path[1] == ":":
            return path

        # 处理根目录
        if path == "/":
            return path

        # 替换分隔符
        return path.replace("/", os.sep)

    def display_directory_content(self, path):
        """在右侧文件列表中显示目录内容"""
        # 清空现有内容
        for item in self.files_list.get_children():
            self.files_list.delete(item)

        # 更新窗口标题
        # self.title(f"文件管理器 - {path}")

        try:
            # 获取目录内容
            items = os.listdir(path)

            # 分离文件夹和文件
            folders = []
            files = []

            for item in items:
                item_path = os.path.join(path, item)
                try:
                    # 获取文件信息
                    stat = os.stat(item_path)
                    modified = time.strftime("%Y-%m-%d %H:%M", time.localtime(stat.st_mtime))

                    if os.path.isdir(item_path):
                        folders.append((item, "文件夹", "", modified, item_path))
                    else:
                        # 获取文件大小
                        size = self.format_file_size(stat.st_size)
                        # 获取文件类型
                        file_ext = os.path.splitext(item)[1].lower()
                        file_type = self.get_file_type(file_ext)
                        files.append((item, file_type, size, modified, item_path))
                except Exception as e:
                    # 无法访问的文件/文件夹
                    if os.path.isdir(item_path):
                        folders.append((f"{item} (无法访问)", "文件夹", "", "", item_path))
                    else:
                        files.append((f"{item} (无法访问)", "文件", "", "", item_path))

            # 排序：文件夹在前，按名称排序
            folders.sort(key=lambda x: x[0].lower())
            files.sort(key=lambda x: x[0].lower())

            # 应用搜索过滤
            if self.search_query:
                query = self.search_query.lower()
                folders = [f for f in folders if query in f[0].lower()]
                files = [f for f in files if query in f[0].lower()]

            # 添加到列表
            for item in folders + files:
                self.files_list.insert("", tk.END, values=(item[0], item[1], item[2], item[3]), tags=(item[4],))

        except Exception as e:
            messagebox.showerror("错误", f"无法显示目录内容: {str(e)}")

    def format_file_size(self, size_bytes):
        """格式化文件大小为人类可读的形式"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes / 1024:.1f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes / (1024 * 1024):.1f} MB"
        else:
            return f"{size_bytes / (1024 * 1024 * 1024):.1f} GB"

    def get_file_type(self, file_ext):
        """根据文件扩展名返回文件类型描述"""
        # 先检查是否为压缩文件
        archive_types = {
            ".zip": "ZIP压缩文件",
            ".tar.gz": "tar.gz压缩文件",
            ".tar.bz2": "tar.bz2压缩文件",
            ".7z": "7z压缩文件"
        }
        if file_ext in archive_types:
            return archive_types[file_ext]

        # 其他文件类型
        ext_types = {
            ".txt": "文本文件",
            ".docx": "Word文档",
            ".xlsx": "Excel表格",
            ".pptx": "PowerPoint演示文稿",
            ".pdf": "PDF文档",
            ".jpg": "JPEG图像",
            ".jpeg": "JPEG图像",
            ".png": "PNG图像",
            ".gif": "GIF图像",
            ".bmp": "BMP图像",
            ".mp3": "音频文件",
            ".wav": "音频文件",
            ".mp4": "视频文件",
            ".avi": "视频文件",
            ".rar": "RAR压缩文件",
            ".py": "Python文件",
            ".html": "HTML文件",
            ".css": "CSS文件",
            ".js": "JavaScript文件",
            ".json": "JSON文件",
            ".xml": "XML文件",
            ".csv": "CSV文件",
        }
        return ext_types.get(file_ext, f"文件 ({file_ext})")

    def on_search_changed(self, *args):
        """搜索内容改变时触发"""
        self.search_query = self.search_var.get().strip()
        if self.current_path:
            self.display_directory_content(self.current_path)

    def on_files_select(self, event):
        """文件列表选择改变时触发"""
        pass

    def on_files_double_click(self, event):
        """双击文件列表项目时触发"""
        item = self.files_list.identify_row(event.y)
        if item:
            # 获取文件路径
            tags = self.files_list.item(item, "tags")
            if tags:
                file_path = tags[0]
                if os.path.isdir(file_path):
                    # 双击文件夹则进入该文件夹
                    self.current_path = file_path
                    self.display_directory_content(file_path)
                    # 在树形视图中找到并选中该文件夹
                    self.expand_tree_to_path(file_path)
                else:
                    # 双击文件则打开
                    self.open_file(file_path)

    def expand_tree_to_path(self, path):
        """在树形视图中展开并选中指定路径"""
        # 转换为标准化路径
        path = os.path.normpath(path)

        # 分割路径
        parts = []
        current = path
        while current and current not in parts:
            parts.insert(0, current)
            current = os.path.dirname(current)
            if current == path:  # 防止无限循环
                break

        # 展开并选中路径
        for part in parts:
            # 转换为树形视图ID格式
            tree_id = part.replace(os.sep, "/").replace(" ", "_").replace(".", "__")
            if self.file_tree.exists(tree_id):
                # 展开节点
                self.file_tree.item(tree_id, open=True)
                # 如果是最后一个部分，则选中
                if part == path:
                    self.file_tree.selection_set(tree_id)
                    # 滚动到视图中
                    self.file_tree.see(tree_id)
            else:
                # 如果节点不存在，则停止
                break

    def add_files(self):
        """添加文件到当前目录"""
        if not self.current_path:
            messagebox.showwarning("警告", "请先选择一个目录")
            return

        # 打开文件选择对话框
        files = filedialog.askopenfilenames(title="选择要添加的文件")

        if files:
            try:
                # 复制文件到当前目录
                for file_path in files:
                    dest_path = os.path.join(self.current_path, os.path.basename(file_path))
                    # 检查文件是否已存在
                    counter = 1
                    while os.path.exists(dest_path):
                        name, ext = os.path.splitext(os.path.basename(file_path))
                        dest_path = os.path.join(self.current_path, f"{name}({counter}){ext}")
                        counter += 1

                    shutil.copy2(file_path, dest_path)

                # 刷新显示
                self.display_directory_content(self.current_path)
                messagebox.showinfo("成功", f"已添加 {len(files)} 个文件")
            except Exception as e:
                messagebox.showerror("错误", f"添加文件失败: {str(e)}")

    def create_folder(self):
        """在当前目录创建新文件夹"""
        if not self.current_path:
            messagebox.showwarning("警告", "请先选择一个目录")
            return

        # 请求文件夹名称
        folder_name = simpledialog.askstring("新建文件夹", "请输入文件夹名称:")

        if folder_name and folder_name.strip():
            folder_name = folder_name.strip()
            folder_path = os.path.join(self.current_path, folder_name)

            # 检查名称是否有效
            if any(c in '/\\:*?"<>|' for c in folder_name):
                messagebox.showerror("错误", "文件夹名称包含无效字符")
                return

            # 检查是否已存在
            if os.path.exists(folder_path):
                messagebox.showerror("错误", "文件夹已存在")
                return

            try:
                # 创建文件夹
                os.makedirs(folder_path)
                # 刷新显示
                self.display_directory_content(self.current_path)
                # 在树形视图中添加新文件夹
                parent_tree_id = self.current_path.replace(os.sep, "/").replace(" ", "_").replace(".", "__")
                if self.file_tree.exists(parent_tree_id):
                    # 移除加载指示器（如果存在）
                    loading_id = f"{parent_tree_id}_loading"
                    if self.file_tree.exists(loading_id):
                        self.file_tree.delete(loading_id)
                    # 添加新文件夹
                    new_folder_id = f"{parent_tree_id}/{folder_name}".replace(" ", "_").replace(".", "__")
                    self.file_tree.insert(parent_tree_id, tk.END, id=new_folder_id, text=folder_name)
                    # 重新排序
                    self.sort_tree_items(parent_tree_id)
            except Exception as e:
                messagebox.showerror("错误", f"创建文件夹失败: {str(e)}")

    def delete_selected(self):
        """删除选中的文件/文件夹"""
        selected = self.files_list.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择要删除的文件/文件夹")
            return

        # 获取选中的文件路径
        file_paths = []
        for item in selected:
            tags = self.files_list.item(item, "tags")
            if tags:
                file_paths.append(tags[0])

        # 确认删除
        if len(file_paths) == 1:
            name = os.path.basename(file_paths[0])
            confirm = messagebox.askyesno("确认删除", f"确定要删除 '{name}' 吗？\n此操作不可恢复！")
        else:
            confirm = messagebox.askyesno("确认删除", f"确定要删除这 {len(file_paths)} 个项目吗？\n此操作不可恢复！")

        if confirm:
            try:
                # 删除文件/文件夹
                deleted = 0
                failed = 0
                for path in file_paths:
                    try:
                        if os.path.isdir(path):
                            shutil.rmtree(path)
                        else:
                            os.remove(path)
                        deleted += 1
                    except Exception as e:
                        failed += 1
                        print(f"删除失败: {path} - {str(e)}")

                # 刷新显示
                self.display_directory_content(self.current_path)

                # 提示结果
                if failed == 0:
                    messagebox.showinfo("成功", f"已成功删除 {deleted} 个项目")
                else:
                    messagebox.showwarning("部分成功", f"已成功删除 {deleted} 个项目，{failed} 个项目删除失败")
            except Exception as e:
                messagebox.showerror("错误", f"删除失败: {str(e)}")

    def rename_selected(self):
        """重命名选中的文件/文件夹"""
        selected = self.files_list.selection()
        if not selected or len(selected) > 1:
            messagebox.showwarning("警告", "请选择一个文件/文件夹进行重命名")
            return

        # 获取文件路径
        item = selected[0]
        tags = self.files_list.item(item, "tags")
        if not tags:
            return

        file_path = tags[0]
        current_name = os.path.basename(file_path)
        directory = os.path.dirname(file_path)

        # 请求新名称
        new_name = simpledialog.askstring("重命名", "请输入新名称:", initialvalue=current_name)

        if new_name and new_name.strip() and new_name != current_name:
            new_name = new_name.strip()
            new_path = os.path.join(directory, new_name)

            # 检查名称是否有效
            if any(c in '/\\:*?"<>|' for c in new_name):
                messagebox.showerror("错误", "名称包含无效字符")
                return

            # 检查是否已存在
            if os.path.exists(new_path):
                messagebox.showerror("错误", "名称已存在")
                return

            try:
                # 重命名
                os.rename(file_path, new_path)
                # 刷新显示
                self.display_directory_content(self.current_path)
                messagebox.showinfo("成功", "重命名成功")
            except Exception as e:
                messagebox.showerror("错误", f"重命名失败: {str(e)}")

    def open_selected(self):
        """打开选中的文件/文件夹"""
        selected = self.files_list.selection()
        if not selected or len(selected) > 1:
            messagebox.showwarning("警告", "请选择一个文件/文件夹进行打开")
            return

        # 获取文件路径
        item = selected[0]
        tags = self.files_list.item(item, "tags")
        if not tags:
            return

        path = tags[0]
        self.open_file(path)

    def open_file(self, path):
        """打开文件或文件夹"""
        try:
            if os.path.isdir(path):
                # 打开文件夹
                self.current_path = path
                self.display_directory_content(path)
                self.expand_tree_to_path(path)
            else:
                # 检查是否为文本文件
                text_extensions = ['.txt', '.py', '.html', '.css', '.js', '.json', '.xml', '.csv', '.md']
                file_ext = os.path.splitext(path)[1].lower()

                if file_ext in text_extensions:
                    # 文本文件，在内部编辑器打开
                    self.open_text_file(path)
                else:
                    # 非文本文件，使用系统默认程序打开
                    if sys.platform.startswith('win'):
                        os.startfile(path)
                    elif sys.platform.startswith('darwin'):  # macOS
                        subprocess.run(['open', path])
                    else:  # Linux
                        subprocess.run(['xdg-open', path])
        except Exception as e:
            messagebox.showerror("错误", f"无法打开: {str(e)}")

    def open_text_file(self, path):
        """在内部编辑器中打开文本文件"""
        try:
            # 显示文本编辑器区域
            self.text_editing_path = path
            self.text_editor_frame.pack(fill=BOTH, expand=YES, pady=5)

            # 更新编辑器标题
            self.text_editor_frame.config(text=f"文本编辑 - {os.path.basename(path)}")

            # 读取文件内容
            with open(path, 'r', encoding='utf-8', errors='replace') as f:
                content = f.read()
                self.text_editor.delete("1.0", tk.END)
                self.text_editor.insert(tk.END, content)

        except Exception as e:
            messagebox.showerror("错误", f"无法打开文本文件: {str(e)}")

    def save_text_file(self):
        """保存文本文件"""
        if not self.text_editing_path:
            return

        try:
            # 获取编辑器内容
            content = self.text_editor.get("1.0", tk.END)

            # 保存到文件
            with open(self.text_editing_path, 'w', encoding='utf-8') as f:
                f.write(content)

            messagebox.showinfo("成功", "文件已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存文件失败: {str(e)}")

    def close_text_editor(self):
        """关闭文本编辑器"""
        self.text_editor_frame.pack_forget()
        self.text_editing_path = None

    def compress_selected(self):
        """压缩选中的文件/文件夹"""
        selected = self.files_list.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择要压缩的文件/文件夹")
            return

        # 获取选中的文件路径
        file_paths = []
        for item in selected:
            tags = self.files_list.item(item, "tags")
            if tags:
                file_paths.append(tags[0])

        if not self.current_path:
            messagebox.showwarning("警告", "请先选择一个目录")
            return

        # 询问压缩文件名和格式
        default_name = "archive"
        if len(file_paths) == 1:
            default_name = os.path.splitext(os.path.basename(file_paths[0]))[0]

        # 创建一个简单的对话框询问压缩参数
        dialog = ttk.Toplevel(self)
        dialog.title("压缩文件")
        dialog.geometry("350x150")
        dialog.transient(self)
        dialog.grab_set()

        # 文件名
        ttk.Label(dialog, text="压缩文件名:").grid(row=0, column=0, padx=10, pady=10, sticky=W)
        name_var = tk.StringVar(value=default_name)
        ttk.Entry(dialog, textvariable=name_var, width=30).grid(row=0, column=1, padx=10, pady=10)

        # 格式选择
        ttk.Label(dialog, text="压缩格式:").grid(row=1, column=0, padx=10, pady=10, sticky=W)
        format_var = tk.StringVar(value="zip")
        format_combo = ttk.Combobox(
            dialog,
            textvariable=format_var,
            values=self.supported_archives,
            state="readonly",
            width=27
        )
        format_combo.grid(row=1, column=1, padx=10, pady=10)

        # 按钮
        result = {"confirmed": False}

        def on_confirm():
            result["confirmed"] = True
            result["name"] = name_var.get()
            result["format"] = format_var.get()
            dialog.destroy()

        btn_frame = ttk.Frame(dialog)
        btn_frame.grid(row=2, column=0, columnspan=2, pady=10)

        ttk.Button(btn_frame, text="确认", command=on_confirm, bootstyle=SUCCESS).pack(side=LEFT, padx=10)
        ttk.Button(btn_frame, text="取消", command=dialog.destroy, bootstyle=SECONDARY).pack(side=LEFT, padx=10)

        self.wait_window(dialog)

        if result["confirmed"] and result["name"]:
            # 构建压缩文件路径
            archive_name = f"{result['name']}.{result['format']}"
            archive_path = os.path.join(self.current_path, archive_name)

            # 检查文件是否已存在
            counter = 1
            while os.path.exists(archive_path):
                archive_path = os.path.join(self.current_path, f"{result['name']}({counter}).{result['format']}")
                counter += 1

            # 在后台线程中执行压缩操作
            threading.Thread(
                target=self.perform_compression,
                args=(file_paths, archive_path, result["format"]),
                daemon=True
            ).start()

    def perform_compression(self, file_paths, archive_path, format):
        """执行压缩操作"""
        try:
            # 显示进度条
            self.after(0, lambda: self.show_progress(f"正在压缩到 {os.path.basename(archive_path)}..."))

            if format == "zip":
                # 压缩为ZIP格式
                with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                    total = len(file_paths)
                    for i, path in enumerate(file_paths):
                        # 更新进度
                        progress = (i + 1) / total * 100
                        self.after(0, lambda p=progress, f=os.path.basename(path):
                        self.update_progress(p, f"正在压缩 {f}..."))

                        # 获取相对路径，避免在压缩文件中包含完整路径
                        if os.path.isdir(path):
                            # 处理文件夹
                            for root, dirs, files in os.walk(path):
                                for file in files:
                                    file_path = os.path.join(root, file)
                                    arcname = os.path.relpath(file_path, os.path.dirname(path))
                                    zipf.write(file_path, arcname)
                        else:
                            # 处理文件
                            arcname = os.path.basename(path)
                            zipf.write(path, arcname)

            elif format in ["tar.gz", "tar.bz2"]:
                # 压缩为tar格式
                mode = f"w:{format.split('.')[1]}"  # "w:gz" 或 "w:bz2"
                with tarfile.open(archive_path, mode) as tarf:
                    total = len(file_paths)
                    for i, path in enumerate(file_paths):
                        # 更新进度
                        progress = (i + 1) / total * 100
                        self.after(0, lambda p=progress, f=os.path.basename(path):
                        self.update_progress(p, f"正在压缩 {f}..."))

                        # 获取相对路径
                        arcname = os.path.basename(path)
                        tarf.add(path, arcname=arcname)

            elif format == "7z":
                # 压缩为7z格式
                with py7zr.SevenZipFile(archive_path, 'w') as z7f:
                    total = len(file_paths)
                    for i, path in enumerate(file_paths):
                        # 更新进度
                        progress = (i + 1) / total * 100
                        self.after(0, lambda p=progress, f=os.path.basename(path):
                        self.update_progress(p, f"正在压缩 {f}..."))

                        # 获取相对路径
                        arcname = os.path.basename(path)
                        if os.path.isdir(path):
                            z7f.writeall(path, arcname)
                        else:
                            z7f.write(path, arcname)

            else:
                self.after(0, lambda: messagebox.showerror("错误", f"不支持的压缩格式: {format}"))
                self.after(0, self.hide_progress)
                return

            # 完成后更新
            self.after(0, self.hide_progress)
            self.after(0, lambda: self.display_directory_content(self.current_path))
            self.after(0, lambda: messagebox.showinfo("成功", f"已成功创建压缩文件: {os.path.basename(archive_path)}"))

        except Exception as e:
            self.after(0, self.hide_progress)
            self.after(0, lambda: messagebox.showerror("压缩失败", f"压缩过程中发生错误: {str(e)}"))

    def extract_selected(self):
        """解压选中的压缩文件"""
        selected = self.files_list.selection()
        if not selected or len(selected) > 1:
            messagebox.showwarning("警告", "请选择一个压缩文件进行解压")
            return

        # 获取选中的文件路径
        item = selected[0]
        tags = self.files_list.item(item, "tags")
        if not tags:
            return

        file_path = tags[0]
        file_name = os.path.basename(file_path)
        file_ext = os.path.splitext(file_name)[1].lower()

        # 检查是否为支持的压缩格式
        ext_to_format = {
            ".zip": "zip",
            ".tar.gz": "tar.gz",
            ".tar.bz2": "tar.bz2",
            ".7z": "7z"
        }

        if file_ext not in ext_to_format:
            messagebox.showwarning("警告", f"不支持的压缩格式: {file_ext}")
            return

        archive_format = ext_to_format[file_ext]

        # 询问解压目录
        default_dir = os.path.splitext(file_name)[0]
        dest_dir = simpledialog.askstring(
            "解压文件",
            "请输入解压目录名称:",
            initialvalue=default_dir
        )

        if not dest_dir or not dest_dir.strip():
            return

        dest_dir = dest_dir.strip()
        dest_path = os.path.join(os.path.dirname(file_path), dest_dir)

        # 检查目录是否已存在
        if os.path.exists(dest_path):
            if not messagebox.askyesno("目录已存在", "目标目录已存在，是否覆盖？"):
                return
            # 删除现有目录
            try:
                shutil.rmtree(dest_path)
            except Exception as e:
                messagebox.showerror("错误", f"无法删除现有目录: {str(e)}")
                return

        # 创建目标目录
        os.makedirs(dest_path, exist_ok=True)

        # 在后台线程中执行解压操作
        threading.Thread(
            target=self.perform_extraction,
            args=(file_path, dest_path, archive_format),
            daemon=True
        ).start()

    def perform_extraction(self, archive_path, dest_path, format):
        """执行解压操作"""
        try:
            # 显示进度条
            self.after(0, lambda: self.show_progress(f"正在解压 {os.path.basename(archive_path)}..."))

            if format == "zip":
                # 解压ZIP文件
                with zipfile.ZipFile(archive_path, 'r') as zipf:
                    file_list = zipf.namelist()
                    total = len(file_list)
                    for i, file in enumerate(file_list):
                        # 更新进度
                        progress = (i + 1) / total * 100
                        self.after(0, lambda p=progress, f=file:
                        self.update_progress(p, f"正在解压 {f}..."))
                        zipf.extract(file, dest_path)

            elif format in ["tar.gz", "tar.bz2"]:
                # 解压tar文件
                mode = f"r:{format.split('.')[1]}"  # "r:gz" 或 "r:bz2"
                with tarfile.open(archive_path, mode) as tarf:
                    file_list = tarf.getnames()
                    total = len(file_list)
                    for i, file in enumerate(file_list):
                        # 更新进度
                        progress = (i + 1) / total * 100
                        self.after(0, lambda p=progress, f=file:
                        self.update_progress(p, f"正在解压 {f}..."))
                    tarf.extractall(dest_path)

            elif format == "7z":
                # 解压7z文件
                with py7zr.SevenZipFile(archive_path, 'r') as z7f:
                    file_list = z7f.getnames()
                    total = len(file_list)
                    for i, file in enumerate(file_list):
                        # 更新进度
                        progress = (i + 1) / total * 100
                        self.after(0, lambda p=progress, f=file:
                        self.update_progress(p, f"正在解压 {f}..."))
                    z7f.extractall(dest_path)

            # 完成后更新
            self.after(0, self.hide_progress)
            self.after(0, lambda: self.display_directory_content(os.path.dirname(archive_path)))
            self.after(0, lambda: messagebox.showinfo("成功", f"已成功解压到: {os.path.basename(dest_path)}"))

        except Exception as e:
            self.after(0, self.hide_progress)
            self.after(0, lambda: messagebox.showerror("解压失败", f"解压过程中发生错误: {str(e)}"))

    def create_widgets(self):
        pass
